package jsonparser.core.parser;

import jsonparser.core.lexer.Token;
import jsonparser.core.lexer.CharReader;
import jsonparser.core.lexer.TokenReader;
import jsonparser.core.parser.nt.Pair;
import jsonparser.core.parser.nt.Value;
import jsonparser.exception.JSONParseException;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

import static jsonparser.core.lexer.Token.*;

/**
 * Created by pendragon on 17-5-20.
 */
public class JSONParser {

    private TokenReader reader;

    public JSONParser(File file){
        CharReader charReader;
        try {
            charReader = new CharReader(new FileReader(file));
            reader = new TokenReader(charReader);
        } catch (FileNotFoundException e) {
            System.out.println("file not found");
        }
    }

    public Builder parse() throws IOException {

        Token start = reader.readNextToken();

        if (start == Token.BEGIN_OBJECT){
            JSONObjectBuilder objectBuilder = parseJSONObject();
            System.out.println("valid");
            return objectBuilder;
        } else if (start == Token.BEGIN_ARRAY){
            JSONArrayBuilder arrayBuilder = parseJSONArray();
            System.out.println("valid");
            return arrayBuilder;
        } else
            throw new JSONParseException("Error: line " + reader.getLine() + ", at index " + reader.getIndex()
                    + " : expected <{> or <[>" );
    }

    private JSONObjectBuilder parseJSONObject() throws IOException{
        JSONObjectBuilder builder = new JSONObjectBuilder();

        Token next = reader.readNextToken();
        if (next == END_OBJECT){
            return builder;
        } else if (next == STRING){
            Pair pair = parsePair();
            builder.buildPair(pair);

            while ((next = reader.readNextToken()) == SEP_COMMA){
                next = reader.readNextToken();
                if (next == STRING)
                    builder.buildPair(parsePair());
                else
                    throw new JSONParseException("Error: line " + reader.getLine() + ", at index " + reader.getIndex()
                            + " : expected <\">" );
            }

            if (next == END_OBJECT){
                return builder;
            } else {
                throw new JSONParseException("Error: line " + reader.getLine() + ", at index " + reader.getIndex()
                        + " : expected <,> or <}>" );
            }
        } else {
            throw new JSONParseException("Error: line " + reader.getLine() + ", at index " + reader.getIndex()
                    + " : expected <\"> or <}>" );
        }
    }

    private Pair parsePair() throws IOException{
        String key = reader.readString();
        if (reader.readNextToken() == SEP_COLON){
            Value value = parseValue(null);
            return new Pair(key, value);
        } else
            throw new JSONParseException("Error: line " + reader.getLine() + ", at index " + reader.getIndex()
                    + " : expected <:>" );
    }

    /**
     * 按理说, 回退的功能应该写在tokenReader里的, 但是为了省时, 我懒得改了, 就这样吧
     * @param moveBackToken 调用程序已经把指针指到Value块的头上了, 这个时候直接用, 不要再next了
     * @return Value块
     * @throws IOException 可能的异常
     */
    private Value parseValue(Token moveBackToken) throws IOException{
        Value value = new Value();

        Token next;
        if (moveBackToken == null)
            next = reader.readNextToken();
        else
            next = moveBackToken;

        switch (next){
            case STRING:
                String str = reader.readString();
                value.setType(Value.ValueType.STRING);
                value.setValue(str);
                break;
            case NUMBER:
                Number num = reader.readNumber();
                value.setType(Value.ValueType.NUMBER);
                value.setValue(num);
                break;
            case BEGIN_OBJECT:
                value.setType(Value.ValueType.JSONOBJECT);
                // recursive
                JSONObjectBuilder objectBuilder = parseJSONObject();
                //todo 加入builder 还是object
                value.setValue(objectBuilder);
                break;
            case BEGIN_ARRAY:
                value.setType(Value.ValueType.JSONARRAY);
                // recursive
                JSONArrayBuilder arrayBuilder = parseJSONArray();
                value.setValue(arrayBuilder);
                break;
            case BOOLEAN:
                Boolean bool = reader.readBoolean();
                value.setType(Value.ValueType.BOOLEAN);
                value.setValue(bool);
                break;
            case NULL:
                reader.readNull();
                value.setType(Value.ValueType.NULL);
                value.setValue(null);
                break;
            default:
                throw new JSONParseException("Error: line " + reader.getLine() + ", at index " + reader.getIndex()
                        + " : illegal <value>" );
        }

        return value;
    }

    private JSONArrayBuilder parseJSONArray() throws IOException{
        JSONArrayBuilder builder = new JSONArrayBuilder();

        Token next = reader.readNextToken();

        if (next == END_ARRAY){
            return builder;
        } else {
            Value value = parseValue(next);
            builder.addValue(value);

            while ((next = reader.readNextToken()) == Token.SEP_COMMA){
                builder.addValue(parseValue(null));
            }

            if (next == END_ARRAY){
                return builder;
            } else
                throw new JSONParseException("Error: line " + reader.getLine() + ", at index " + reader.getIndex()
                        + " : expected <]>" );
        }
    }
}
